﻿<Serializable()> Public Class tunnelSection

    Public Radius As Double = 2.75

    ''' <summary>
    ''' 初始化，SectPnts读入断面观测点
    ''' </summary>
    ''' <remarks></remarks>
    Public SectPnts As TunnelSectionPoints
    ''' <summary>
    ''' 底块中心：以底块圆弧两侧两点及指定半径计算的底块圆心 
    ''' </summary>
    ''' <remarks></remarks>
    Public ButtomCenter As TSPCoor
    Public SectNo As String
    Public Four_circle_center As TSPCoor

    Sub New(ByVal path As String)
        SectPnts = New TunnelSectionPoints
        SectPnts.Read(path)
        CalButtomCenter()
        SectNo = SectPnts.SectionNo
        If SectPnts.F_Note = True Then '如果测得的数据里边有F作为标记的话
            F_Section = SectPnts.Points.FindAll(New Predicate(Of SectionPoint)(AddressOf Find_F_Note))
            F_Section.Sort(New Comparison(Of SectionPoint)(AddressOf Sort_By_Hv)) ' 排序

            '用F标记过的点的角度,来替换angels()数组,并把这些F点在数据中剔除
            If F_Section.Count = 4 Then
                For i As Integer = 0 To F_Section.Count - 1
                    angles(i + 1) = F_Section(i).Hv
                    temp_id = F_Section(i).Name
                    Dim index As Integer = SectPnts.Points.FindIndex(AddressOf find_duplicate_pt)
                    SectPnts.Points.RemoveAt(index)
                Next
            ElseIf F_Section.Count = 6 Then
                For i As Integer = 0 To F_Section.Count - 1
                    angles(i) = F_Section(i).Hv
                    temp_id = F_Section(i).Name
                    Dim index As Integer = SectPnts.Points.FindIndex(AddressOf find_duplicate_pt)
                    SectPnts.Points.RemoveAt(index)
                Next
            Else
                SectPnts.F_Note = False
            End If


        End If

    End Sub

    Sub CalButtomCenter()
        '找到两侧最下边点 ==> 计算圆心
        '最下边垂直角应为180度
        If (SectPnts.Points.Count = 0) Then
            Return
        End If



        '找到两侧最下边点 ==> 计算圆心
        '最下边垂直角应为180度
        Dim vP_Near180U As New TSPCoor(0, 0) '大于180度的最小者
        Dim vP_Near180L As New TSPCoor(0, 0) '小于180度的最大者
        If SectPnts.FindNear180(vP_Near180U, vP_Near180L) Then
            Dim arc As New tunnelArc
            If arc.ArcDef_By_2P_And_R(vP_Near180L, vP_Near180U, Radius, True) Then
                ButtomCenter = arc.Center
            End If
        End If

        For Each vP As SectionPoint In SectPnts.Points
            vP.Hv = vP.AzimFrom(ButtomCenter)
        Next
        SectPnts.SortByHv()
    End Sub


    ''' <summary>
    ''' 断面数据输出
    ''' 点号，垂直角，斜距，断面号，里程，水平向坐标X，竖直向坐标Y，仪器高H
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Overrides Function ToString() As String
        Return SectPnts.ToString()
    End Function





    ''' <summary>
    ''' 求弧段上有效的观测点
    ''' </summary>
    ''' <remarks></remarks>
    Sub GetValidPointOnArc(ByVal CenterAzim As Double, ByVal HalfWidth As Double, ByVal ValidPointIndex As List(Of Integer), ByVal StdErr As Double)
        For Each vP As SectionPoint In SectPnts.Points
            Dim az As Double = SvAngle.CNearZero(vP.AzimFromCenter - CenterAzim)
            If Math.Abs(az) > HalfWidth Then
                vP.IsValid = False
            Else
                vP.IsValid = True
            End If
        Next

        Dim mArc As New tunnelArc
        Dim Errs(0) As Double, StandErr As Double
        If mArc.CircleDefineByPoints(Me.SectPnts.Points) Then
            mArc.Cal_Err_From_Cir(SectPnts, Errs, StandErr)
        End If

        For Each vp As SectionPoint In SectPnts.Points
            If vp.IsValid Then
                'If Errs(i) Then

                'End If
            End If
        Next



    End Sub

    ''' <summary>
    ''' 几何中心：以剔除明显精差后的断面点拟合的圆心为几何中心
    ''' </summary>
    ''' <remarks></remarks>
    Public GeometryCenter As TSPCoor






    ''' <summary>
    ''' 拟定中心：初算时采用全部点拟合 
    ''' </summary>
    ''' <remarks></remarks>
    Public DesignCenter As TSPCoor
    Sub ArcSimulation()
        Dim arc As New tunnelArc
        Dim arcCen As TSPCoor
        Dim Errs(0) As Double, StandErr As Double
        arc.CircleDefineByPoints(Me.SectPnts.Points)

        arc.Cal_Err_From_Cir(SectPnts, Errs, StandErr)
        arcCen = arc.Center
        For Each pt As SectionPoint In SectPnts.Points
            pt.AzimFrom(DesignCenter)
        Next
    End Sub


    Private Sub ArcSimulationOnce()
        Dim pntCoors As New List(Of TSPCoor)
        For Each pt As SectionPoint In SectPnts.Points
            If pt.IsValid Then
                Dim coor As New TSPCoor(pt.Coor.Coor_V, pt.Coor.Coor_H)
                pntCoors.Add(coor)
            End If
        Next

        Dim arc As New tunnelArc
        Dim Errs(0) As Double, StandErr As Double
        arc.CircleDefineByPoints(Me.SectPnts.Points)
        Dim StdErr As Double = 0
        arc.Cal_Err_From_Cir(SectPnts, Errs, StandErr)



    End Sub

    '剔除粗差,当平差时大于0.2m的测量点,都会被删除,(0.2是否合理有待考查)
    Public Sub Kill_Main_Err()
        Dim arc As tunnelArc = New tunnelArc()
        arc.Radius = 2.75
        arc.Center = Me.ButtomCenter
        Dim err(0) As Double
        Dim StdErr As Double = 0
        arc.Cal_Err_From_Cir(Me.SectPnts, err, StdErr)
        For i As Integer = SectPnts.Points.Count - 1 To 0 Step -1
            If Math.Abs(err(i)) > 0.2 Then
                SectPnts.Points.RemoveAt(i)
            End If
        Next
    End Sub

    '环片的设计角度,跟实际应该是有出入滴!!所以需要按F打出缝来,以后就按照这些个点的角度来计算夹缝
    Private angles() As Double = {42 * Math.PI / 180, 107 * Math.PI / 180, 172 * Math.PI / 180, 188 * Math.PI / 180, 253 * Math.PI / 180, 318 * Math.PI / 180}
    Public F_Section As List(Of SectionPoint) = New List(Of SectionPoint)
    ''分别对每个环片,剔除粗差
    Public Function eleminate_Err() As List(Of TunnelSectionPoints)
        ''声明一个list储存5段弧
        Dim section_lists As List(Of TunnelSectionPoints) = New List(Of TunnelSectionPoints)(5)

       

        For i As Integer = 0 To 4
            If SectPnts.F_Note = False Then
                If i = 2 Then
                    start_hv = angles(i) - 2 * Math.PI / 180
                    end_hv = angles(i + 1) + 2 * Math.PI / 180
                Else
                    start_hv = angles(i) - 5 * Math.PI / 180
                    end_hv = angles(i + 1) + 5 * Math.PI / 180
                End If
            Else
                start_hv = angles(i)
                end_hv = angles(i + 1)
            End If
            Dim each_SectionPts As New TunnelSectionPoints()
            Dim Each_Sections As List(Of SectionPoint) = New List(Of SectionPoint)
            Each_Sections = SectPnts.Points.FindAll(New Predicate(Of SectionPoint)(AddressOf Find_Start_End_byHv))
            Each_Sections.Sort(New Comparison(Of SectionPoint)(AddressOf Sort_By_Hv))

            each_SectionPts.Points = Each_Sections
            If i <> 2 Then
                kill_mistake(each_SectionPts)
            Else
                each_SectionPts = kill_mistake_special(each_SectionPts, True)
            End If

            section_lists.Add(each_SectionPts)
        Next
        Merge_List(section_lists)
        Dim four_h, four_v As Double
        four_h = 0
        four_v = 0
        For i As Integer = 0 To section_lists.Count - 1
            If i <> 2 Then
                If section_lists(i).center Is Nothing Then
                    Exit For
                End If
                four_h += section_lists(i).center.Coor_H
                four_v += section_lists(i).center.Coor_V
            End If
        Next
        four_h = four_h / 4
        four_v = four_v / 4
        Four_circle_center = New TSPCoor(four_v, four_h)
        ReCount_Start_End(section_lists)
        Return section_lists
    End Function
    Private Sub ReCount_Start_End(ByRef lists As List(Of TunnelSectionPoints))
        If SectPnts.F_Note = True Then
            If F_Section.Count = 4 Then
                F_Section.Insert(0, lists(0).Points(0))
                F_Section.Add(lists(4).Points(lists(4).Points.Count - 1))
            End If
        End If
        If Four_circle_center Is Nothing Or Four_circle_center.Coor_H = 0 Then
            Return
        End If
        For i As Integer = 0 To lists.Count - 1
            If SectPnts.F_Note = False Then
                If lists(i).Points.Count > 0 Then
                    lists(i).start_hv = lists(i).Points(0).AzimFrom(Four_circle_center)
                    lists(i).end_hv = lists(i).Points(lists(i).Points.Count - 1).AzimFrom(Four_circle_center)
                End If
            Else
                If lists(i).Points.Count > 0 Then
                    lists(i).start_hv = F_Section(i).AzimFrom(Four_circle_center)
                    lists(i).end_hv = F_Section(i + 1).AzimFrom(Four_circle_center)
                End If
            End If

        Next

    End Sub

    '找到重复名称的点,删掉其中一个,删除标准是同"标称半径(2.75)"比较,谁大就删谁
    Private Sub Merge_List(ByRef lists As List(Of TunnelSectionPoints))
        ''''''有可能有的list里没有数据

        For i As Integer = 0 To lists.Count - 1
            If lists(i).Points.Count = 0 And i <> 2 Then
                Return
            End If
        Next

        '*********先找重合的,然后删删删删删
        If SectPnts.F_Note = False Then

            Dim count As Integer
            For i As Integer = 1 To lists.Count - 1
                count = lists(i).Points.Count
                Dim j As Integer = 0
                Do While (j < count)
                    '此时把该点的名称赋给temp_id这个全局变量
                    temp_id = lists(i).Points(j).Name
                    '储存该点在merge中的index
                    Dim index As Integer = lists(i - 1).Points.FindIndex(AddressOf find_duplicate_pt)
                    If index < 0 Then
                        '如果不存在,就把这个点添加到merge里去
                        'merge.Points.Add(lists(i).Points(j))

                    Else
                        '否则,如果该点是重复的,则取他们中dist_ERR较小的一个点
                        If Math.Abs(lists(i).Points(j).Err_Distance) < Math.Abs(lists(i - 1).Points(index).Err_Distance) Then

                            lists(i - 1).Points.RemoveAt(index)
                        Else
                            lists(i).Points.RemoveAt(j)
                            j = j - 1
                            count = count - 1
                        End If
                    End If
                    j += 1
                Loop

            Next


            '****删完之后呢,就重新拟合下半径,计算中心,和起始,终止弧度,以及每个点的残差
            For i As Integer = 0 To lists.Count - 1
                Dim arc As tunnelArc = New tunnelArc()
                If i <> 2 Then
                    arc.CircleDefineByPoints(lists(i).Points)
                    Dim Errs(0) As Double, StandErr As Double
                    Dim StdErr As Double = 0
                    arc.Cal_Err_From_Cir(lists(i), Errs, StandErr)
                    ' 跟标准的圆拟合的误差
                    lists(i).Call_Arc_Err()
                Else
                    If lists(i).Points.Count <> 0 Then
                        '跟自己的圆拟合的误差
                        lists(i).Call_Arc_Err1()
                    End If

                End If
            Next
        End If


        Dim merge As TunnelSectionPoints = New TunnelSectionPoints
        For i As Integer = 0 To 4
            For j As Integer = 0 To lists(i).Points.Count - 1
                merge.Points.Add(lists(i).Points(j))
            Next
        Next
        SectPnts = merge


    End Sub

    '如果有重复的name,那么就用find函数把狗日的找出来
    Dim temp_id As String
    Private Function find_duplicate_pt(ByVal P As SectionPoint) As Boolean
        If P.Name = temp_id Then
            Return True
        Else
            Return False
        End If
    End Function

    '对每一段圆弧剔粗差
    Private Sub kill_mistake(ByRef section_pts As TunnelSectionPoints)

        If section_pts.Points.Count < 3 Then
            Return
        End If
        Dim backup_sectionpoints As New TunnelSectionPoints
        backup_sectionpoints = section_pts.Get_Clone()
        ' Dim Each_Sections As List(Of SectionPoint) = section_pts.Points
        Dim arc As tunnelArc = New tunnelArc()
        arc.CircleDefineByPoints(section_pts.Points)
        Dim Errs(0) As Double, StandErr As Double
        Dim StdErr As Double = 0
        arc.Cal_Err_From_Cir(section_pts, Errs, StandErr)
        Dim count As Integer = 0
        Dim limit As Integer = section_pts.Points.Count * 0.5
        '在这里,收敛的条件是中误差小于1毫米或者剔除的次数大于6次,因为很有可能点的离散度太大
        '以至于剔到中误差为1毫米时, 根本就没几个点剩下
        '这里有个先决条件:集合当中,正确的点的数目要大于错误点的数目(事实上采集回来的数据基本如此,但是未能排除例外)
        ''*&*********************
        Do While StandErr > 0.00001
            Dim max_er As Double = 0
            Dim j As Integer = 0
            For i As Integer = 0 To Errs.Length - 1
                If Math.Abs(Errs(i)) > max_er Then
                    max_er = Math.Abs(Errs(i))
                    j = i
                End If
            Next
            section_pts.Points.RemoveAt(j)
            arc.CircleDefineByPoints(section_pts.Points)
            arc.Cal_Err_From_Cir(section_pts, Errs, StandErr)
        Loop
        section_pts.Call_Arc_Err()
        If section_pts.Points.Count < limit Then
            section_pts = kill_mistake_special(backup_sectionpoints, False)
        End If

    End Sub

    '找3个点,拟合最接近2.75的圆,然后再限定来剔除粗差,bool看是否为顶环
    Private Function kill_mistake_special(ByVal section_pts As TunnelSectionPoints, ByVal bool As Boolean) As TunnelSectionPoints
        Dim fine_r As Double = 0
        Dim center As TSPCoor = Nothing
        If section_pts.Points.Count < 3 Then
            section_pts.Points.Clear()
            Return Nothing
        End If
        Dim backup_sectionpoints As New TunnelSectionPoints
        backup_sectionpoints = section_pts.Get_Clone()
        For i As Integer = 0 To section_pts.Points.Count - 3
            For j As Integer = i + 1 To section_pts.Points.Count - 2
                For k As Integer = j + 1 To section_pts.Points.Count - 1
                    Dim ar As tunnelArc = New tunnelArc()
                    ar.ArcDefBy3P(section_pts.Points(i).Coor, section_pts.Points(j).Coor, section_pts.Points(k).Coor)
                    If Math.Abs(ar.Radius - 2.75) < Math.Abs(fine_r - 2.75) And ar.Center.Coor_V < 5 Then
                        fine_r = ar.Radius
                        center = ar.Center
                    End If
                Next
            Next
        Next
        section_pts.Radius = fine_r
        section_pts.center = center
        If center Is Nothing Then
            Throw New Exception
        End If
        Dim st As Double = 0
        If (bool = True) Then
            If SectPnts.F_Note = True Then
                st = 0.005
            Else
                st = 0.008
            End If
        Else
            st = 0.003
        End If
        For i As Integer = section_pts.Points.Count - 1 To 0 Step -1
            Dim v As SvVector = New SvVector(center, section_pts.Points(i).Coor)
            section_pts.Points(i).Dist_frm_Center = v.Distance
            section_pts.Points(i).Err_Distance = v.Distance - fine_r
            If Math.Abs(section_pts.Points(i).Err_Distance) > st Then
                section_pts.Points.RemoveAt(i)
            End If
        Next
        If bool = False Then
            Dim arc As tunnelArc = New tunnelArc()
            arc.CircleDefineByPoints(section_pts.Points)
            Dim Errs(0) As Double, StandErr As Double
            Dim StdErr As Double = 0
            arc.Cal_Err_From_Cir(section_pts, Errs, StandErr)
        Else
            section_pts.Call_Arc_Err1()
        End If
        If bool = False And section_pts.Points.Count <= backup_sectionpoints.Points.Count * 0.4 Then
           section_pts.Points.Clear()
        End If
        If Math.Abs(fine_r - 2.75) > 0.3 Or section_pts.Points.Count = 3 Then
            section_pts.Points.Clear()
        End If
        Return section_pts
    End Function

    Private start_hv As Double
    Private end_hv As Double
    Private Function Find_Start_End_byHv(ByVal P As SectionPoint) As Boolean
        If P.Hv < end_hv And P.Hv > start_hv Then
            Return True
        Else
            Return False
        End If
    End Function
    Private Function Find_F_Note(ByVal P As SectionPoint) As Boolean
        If P.Name.Contains("F") = True Or P.Name.Contains("f") = True Then
            Return True
        Else
            Return False
        End If
    End Function
    Private Function Sort_By_Hv(ByVal p1 As SectionPoint, ByVal p2 As SectionPoint) As Integer
        If p1.Hv < p2.Hv Then
            Return -1
        ElseIf p1.Hv = p2.Hv Then
            Return 0
        Else
            Return 1
        End If
    End Function





End Class
